가비지 컬렉션
가비지 컬렉션은 메모리 관리 기법 중의 하나로 사용자가 메모리를 관리하는 기법이 아닌 가비지 컬렉터에게 메모리 관리를 맡기는 방법이다. C언어 같은 경우 malloc으로 할당한 메모리의 경우 사용자가 사용하지 않을 때 free를 통해 임의적으로 할당 해주어야 하는데 이렇게 메모리 관리를 사용자에게 맡기는 경우 메모리 누수가 생기거나 해제한 메모리를 다시 사용하는 많은 문제점들이 양산되고는 했다.
가비지 컬렉터는 새 메모리 영역을 사용할 수 없을 정도로 메모리를 많이 사용했다고 판단될 때 동작하여 쓰이지 않는 메모리를 찾아 모두 할당해제 해준다. 이 때 가비지 컬렉터가 할당해제 하기 위해 동작하는 동안 다른 모든 쓰레드들이 중지되는 데 이를 ‘Stop the World’라고 부른다. (가비지 컬렉터도 쓰레드 위에서 실행되지만 이 쓰레드를 제외한 모든 쓰레드가 멈춘다).
가비지 컬렉터가 참조되지 않는 객체를 판단하여 삭제하는 것은 Rechability를 판단하여 삭제하게 된다. java에서 어떠한 객체를 생성한 후 이 객체를 참조하기 위해서 참조하게 되는데 이 객체를 최초 가리키고 있는 것을 Root Set이라고 하며 이 RootSet을 통해 객체가 참조하고 있는 객체들을 따라간다.
1 |
|
a 라는 참조변수는 A의 객체를 참조하고 또 자신의 멤버 변수인 b가 할당된 B를 참조하고 이 B는 C를 참조한다. 이러한 식으로 최초 참조되는 Root set만 알고 있으면 참조하는 객체들을 따라갈 수 있고 이를 Rechable 닿을 수 있다라고 판단하며 메모리에서 할당해제 하지 않는다. 즉 가비지 컬렉터에게 할당 해제되는 메모리는 UnRechable한 객체들이다.
자바는 이러한 UnRechable한 객체들에 대해 가비지 컬렉션을 수행하기 위해 가설을 세웠는데 새로운 객체는 금방 UnRechable해진다는 것이다. 즉 새로 생겨난 객체일수록 UnReChable할 가능성이 높다는 것이다. 이러한 개념을 적용하기 위해 물리적 공간을 2개로 나누었는데 이 것이 Young 영역과 Old 영역이다.
즉 새로 생성된 객체는 Young 영역에 있으며 Minor GC의 영향을 받다가 오래 살아남으면 Old 영역으로 이동하여 Major GC의 영향을 받게 된다.
####Young 영역
Young 영역은 3개의 영역으로 나뉘는데 Eden 영역과 Survivor0, Survivor2영역이다. Young 영역에서 새로 생성된 개체들은 이 메모리 공간을 이동하며 살아있다가 Old 영역으로 이동한다. Young 영역에서 일어나는 과정은 다음과 같다.
- 새로 생성된 객체는 Eden에 위치한다.
- Eden에서 GC가 한번 발생한 후 살아남은 객체는 Survivor 영역으로 이동한다.
- Survivor 영역에 객체들이 계속 쌓인다.
- 하나의 Survivor 영역이 가득차게 되면 이 중에서 살아남은 객체를 다른 Survivor 영역으로 옮긴다. 이 과정에서 하나의 Survivor 영역은 비워져있어야 한다. -> 채워지는 곳은 한곳 뿐
- 이 과정에서 계속 살아남으며 Survivor에 남아있는 객체는 Old 영역으로 이동한다.
Old 영역
Old 영역은 기본적으로 데이터가 가득차면 GC를 실행한다. GC 방식에 따라서 처리 절차가 달라지며 다음과 같은 방식의 GC가 있다.
- Serial GC
- Parallel GC
- Parallel Old GC
- Concurrent Mar & Sweep GC
- G1 GC
Serial GC
Old 영역에서 GC는 Mark - Sweep - Compact 라는 알고리즘을 사용한다. 살아있는 객체를 식별(Mark)하고 살아있는 것만 남기고(Sweep) 마지막 단계에서는 각 객체들이 연속적으로 쌓이도록 힙의 앞쪽으로 객체들을 당긴다 (Compat)
Serial GC는 적은 메모리와 CPU 코어 개수가 적을 때 적합한 방식이다.
Parallel GC
Parallel GC는 Serial GC와 기본적으로 같은 알고리즘을 가진다. 그러나 Serial GC는 GC를 처리하는 스레드가 하나인 데에 비해 Parallel GC는 여러개의 쓰레드를 가지고 작동한다. Parallel GC는 메모리가 충분하고 코어의 개수가 많을 때 사용한다.
Parallel Old GC
Parallel Old GC는 Old GC에서의 알고리즘이 다른데 Mark -Summary - Compaction 단계를 거친다. Summary 단계에서 별도의 살아있는 객체를 식별하는 Sweep 단계보다 복잡한 알고리즘을 거친다.
Concurrent Mar & Sweep GC
CMS GC는 복잡한 과정을 거친다. CMS는 Initial Mark단계에서 클래스 로더에 가장 가까운 객체를 찾는다. 그다음 ConCurrent Mark단계에서 방금 전에 살아 있다고 확인한 객체에서 참조하고 있는 객체들을 따라가면서 확인한다. 이 단계에서 특징은 다른 스레드가 동작하고 있는 상태에서 동작한다. 그다음 Remark 단계에서는 ConCurrent Mark에서 새로 추가되거나 참조가 끊긴 객체를 확인한다. 마지막으로 Concurrent Sweep 단계에서 쓰레기를 정리한다. 이러한 단계로 진행되기 때문에 Stop-the-World시간이 매우 짧아 반응성이 중요시 되는 애플리케이션에 적합하다. 하지만 Compaction을 하지 않기 때문에 조각난 메모리가 발생할 가능성이 크다.
G1 GC
이는 기존의 GC방법가 다른 GC 방법으로 바둑판의 영역에 객체를 할당하고 GC를 실행한다. 그러다가 해당 영역이 차면 다른 영역에서 객체를 할당하고 GC를 실행한다.